//
//  NetworkingError.swift
//  MoyaDemo
//
//  Created by hsbcnet.mobile.uk hsbcnet.mobile.uk on 2019/5/9.
//  Copyright © 2019 chensx. All rights reserved.
//

import Foundation
import Alamofire
import Moya

public enum NetworkingError: Swift.Error {
    /// Indicates a response failed to map to an image.
    case imageMapping(Response)
    
    /// Indicates a response failed to map to a JSON structure.
    case jsonMapping(Response)
    
    /// Indicates a response failed to map to a String.
    case stringMapping(Response)
    
    /// Indicates a response failed to map to a Decodable object.
    case objectMapping(Swift.Error, Response)
    
    /// Indicates that Encodable couldn't be encoded into Data
    case encodableMapping(Swift.Error)
    
    /// Indicates a response failed with an invalid HTTP status code.
    case statusCode(Response)
    
    /// fails to create a valid `URL`.
    case invalidURL
    
    /// when a parameter encoding object throws an error during the encoding process.
    case parameterEncodingFailed(Swift.Error)
    
    /// Indicates a response failed due to an underlying `Error`.
    case underlying(Swift.Error, Response?)
    
}


public extension NetworkingError {
    
    init(error: Moya.MoyaError) {
        switch error {
        case .imageMapping(let response): self = .imageMapping(response)
        case .jsonMapping(let response): self = .jsonMapping(response)
        case .stringMapping(let response): self = .stringMapping(response)
        case .objectMapping(let error, let response): self = .objectMapping(error, response)
        case .encodableMapping(let error): self = .encodableMapping(error)
        case .statusCode(let response): self = .statusCode(response)
        case .requestMapping: self = .invalidURL
        case .parameterEncoding(let error): self = .parameterEncodingFailed(error)
        case .underlying(let error, let response):
            if let afError = error as? AFError {
                self = NetworkingError(afError: afError, response: response)
            }
            else if let moyaError = error as? MoyaError {
                self = NetworkingError(error: moyaError)
            }
            else {
                self = .underlying(error, response)
            }
            
        }
    }
    
    init(afError: AFError, response: Response?) {
        guard let newResponse = response else {
            self = .underlying(afError, response)
            return
        }
        switch afError {
        case .invalidURL(_): self = .invalidURL
        case .parameterEncodingFailed(_): self = .parameterEncodingFailed(afError)
        case .multipartEncodingFailed(_): self = .underlying(afError, nil)
        case .responseValidationFailed(let reason):
            switch reason {
            case .unacceptableStatusCode(_): self = .statusCode(newResponse)
            default: self = .underlying(afError, response)
            }
        case .responseSerializationFailed(let reason):
            switch reason {
            case .stringSerializationFailed(_): self = .stringMapping(newResponse)
            case .jsonSerializationFailed(_): self = .jsonMapping(newResponse)
            default: self = .underlying(afError, newResponse)
            }
        default: self = .underlying(afError, response)
        }
    }
}

extension NetworkingError {
    static func getUnderlyingErrorCode(error: Swift.Error) -> Int {
        if error is NetworkingError {
            if case let .underlying(error, _) = (error as! NetworkingError) {
                // 适配Alamofire 5.0
                if (error as? AFError)?.isExplicitlyCancelledError == true {
                    return NSURLErrorCancelled
                }
            }
            if case let .statusCode(response) = (error as! NetworkingError) {
                return response.statusCode
            }
        }
        if error is MoyaError {
            if case let .underlying(error, _) = (error as! MoyaError) {
                // 适配Alamofire 5.0
                if (error as? AFError)?.isExplicitlyCancelledError == true {
                    return NSURLErrorCancelled
                }
            }
            return ((error as? MoyaError)?.errorUserInfo[NSUnderlyingErrorKey] as? NSError)?.code ?? NSURLErrorUnknown
        }
        if (error as? AFError)?.isExplicitlyCancelledError == true {
            return NSURLErrorCancelled
        }
        return (error as NSError).code
    }
    
    static func getUnderlyingError(error: Swift.Error) -> Swift.Error? {
        if error is MoyaError {
            return (error as? MoyaError)?.errorUserInfo[NSUnderlyingErrorKey] as? NSError
        }
        return (error as NSError)
    }
}

